package io.coderyeah.basic.interceptor;

import io.coderyeah.basic.annotation.PreAuthorize;
import io.coderyeah.basic.jwt.JwtUtils;
import io.coderyeah.basic.jwt.LoginData;
import io.coderyeah.basic.jwt.Payload;
import io.coderyeah.basic.jwt.RsaUtils;
import io.coderyeah.basic.util.LoginInfoHolder;
import io.coderyeah.org.mapper.EmployeeMapper;
import io.coderyeah.user.domain.LoginInfo;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.security.PublicKey;
import java.util.List;

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private EmployeeMapper employeeMapper;

    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        // 静态资源放行
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        //1.获取token
        String token = req.getHeader("token");
        //3.如果有token，通过token获取redis的登录信息
        if (token != null) {
            LoginInfo info = null;
            try {
                // （私钥加密）获取公钥解密
                final PublicKey publicKey = RsaUtils.getPublicKey(LoginInterceptor.class.getClassLoader().getResource("auth_rsa.pub").getFile());
                // 获取用户信息
                final Payload<LoginData> payload = JwtUtils.getInfoFromToken(token, publicKey, LoginData.class);
                info = payload.getLoginData().getLoginInfo();
            } catch (ExpiredJwtException e) { //jwt过期时抛出的异常
                resp.setContentType("application/json;charset=UTF-8");
                resp.getWriter().println("{\"success\":false,\"message\":\"timeOut\"}");
                return false;
            }

            if (info != null) {//登录成功，而且没有过期
                //5.如果登录信息不为null - 放行 + 刷新过期时间[重新添加到redis]
                LoginInfoHolder.set(info);
                if (info.getType() == 1) {
                    // 前端用户直接放行
                    return true;
                }
                // 后端管理员 需要判断权限
                final HandlerMethod handlerMethod = (HandlerMethod) handler;
                // 获取访问的方法上的权限注解
                final PreAuthorize p = handlerMethod.getMethodAnnotation(PreAuthorize.class);
                if (null == p) {
                    // 此方法上没有该注解直接放行 公用资源
                    return true;
                }
                // 需要判断该登录用户是否具备此接口的操作权限
                final String sn = p.sn();
                // 查询该登录用户的所有权限集合
                List<String> ownPermissions = employeeMapper.getPermissionSnByLoginInfoId(info.getId());
                if (ownPermissions.contains(sn)) {
                    // 包含该权限
                    return true;
                }
                // 没有权限返回json信息给客户端
                resp.setCharacterEncoding("UTF-8");
                resp.setContentType("application/json;charset=utf-8");
                final PrintWriter writer = resp.getWriter();
                writer.print("{\"success\":false,\"message\":\"noPermission\"}");
                writer.close();
                return false;
            }
        }
        //2.判断token，如果为null - 直接拦截 响应前端 - 跳转到登录页面
        //4.如果登录信息为null - 过期了 直接拦截 响应前端 - 跳转到登录页面

        //跳转到登录页面 - 后端跳不了，因为后端项目没有页面 - 放在前端跳转
        //告诉浏览器我要给你响应一个json数据，编码集为utf-8
        resp.setContentType("application/json;charset=UTF-8");
        resp.getWriter().println("{\"success\":false,\"message\":\"noLogin\"}");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        LoginInfoHolder.remove();
    }
}
